/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "subcmd.h"

static void requeuejob_usage(const char *);

/**
 * @brief       qp job requeue handler, to requeue a job.
 *
 * @param[in]   argc          #1: number of input argument
 * @param[in]   argv          #2: string array of the input argument
 *
 * @retval      0             #1: succeed
 * @retval      1             #2: input option is wrong or kind of input argument not specified
 * @retval      2             #3: argument has syntax error
 * @retval      3             #4: failed to requeue the job
 */
int
cmd_requeue_job(int argc, char **argv)
{
    int options=REQUEUE_RUN;
    int status=JOB_STATUS_PEND;

    char *strQueue=NULL, *strNode=NULL, *strUser=NULL, *strJobName=NULL;
    char *strProject=NULL;
    while (TRUE) {
        int option_index = 0;
        static struct option long_options[] = {
            {"help", no_argument, 0, 'h'},
            {"all", no_argument, 0, 'a'},
            {"done", no_argument, 0, 'd'},
            {"exit", no_argument, 0, 'e'},
            {"start", no_argument, 0, 'r'},
            {"hold", no_argument, 0, 'H'},
            {"queue", required_argument, 0, 'q'},
            {"node", required_argument, 0, 'm'},
            {"user", required_argument, 0, 'u'},
            {"jobname", required_argument, 0, 'J'},
            {"project", required_argument, 0, 'P'},
            {NULL, 0, 0, 0}
        };

        int opt;
        opt = getopt_long(argc, argv, "haderHq:m:u:J:P:", long_options, &option_index);
        if (-1 == opt) {
            break;
        }

        switch (opt) {
        case 'a':
            options |= (REQUEUE_DONE | REQUEUE_EXIT | REQUEUE_RUN);
            break;
        case 'd':
            options |= REQUEUE_DONE;
            break;
        case 'e':
            options |= REQUEUE_EXIT;
            break;
        case 'r':
            options |= REQUEUE_RUN;
            break;
        case 'H':
            status = JOB_STATUS_PSUSP;
            break;
        case 'q':
            if (NULL != strQueue) {
                fprintf(stderr, "Do not specify -q/--queue option for multiple times.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            if (0 >= strlen(optarg)) {
                fprintf(stderr, "Specify a queue name with -q/--queue option.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            strQueue = optarg;
            break;
        case 'm':
            if (NULL != strNode) {
                fprintf(stderr, "Do not specify -m/--node option for multiple times.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            if (0 >= strlen(optarg)) {
                fprintf(stderr, "Specify a node name with -m/--node option.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            strNode = optarg;
            break;
        case 'u':
            if (NULL != strUser) {
                fprintf(stderr, "Do not specify -u/--user option for multiple times.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            if (0 >= strlen(optarg)) {
                fprintf(stderr, "Specify a user name with -u/--user option.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            strUser = optarg;
            break;
        case 'J':
            if (NULL != strJobName) {
                fprintf(stderr, "Do not specify -J/--jobname option for multiple times.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            if (0 >= strlen(optarg)) {
                fprintf(stderr, "Specify a job name with -J/--jobname option.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            strJobName = optarg;
            break;
        case 'P':
            if (NULL != strProject) {
                fprintf(stderr, "Do not specify -P/--project option for multiple times.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            if (0 >= strlen(optarg)) {
                fprintf(stderr, "Specify a project name with -P/--project option.\n");
                requeuejob_usage(argv[0]);
                exit(1);
            }

            strProject = optarg;
            break;
        case 'h':
            requeuejob_usage(argv[0]);
            exit(0);
        case '?':
        default:
            requeuejob_usage(argv[0]);
            exit(1);
        }
    }

    if (argc == optind && NULL == strUser && NULL == strQueue
        && NULL == strNode && NULL == strJobName && NULL == strProject) {

        fprintf(stderr, "Job ID or one of '-m/--node', '-u/--user' '-q/--queue' and '-P/--project' must be specified.\n");
        requeuejob_usage(argv[0]);

        exit(2);
    }

    struct request requeRequest;
    memset(&requeRequest, 0, sizeof(struct request));

    int numJobIds;
    JOBID_T *jobIds = NULL;
    numJobIds = get_jobids_fromargv(argc, argv, &jobIds);
    if (0 < numJobIds) { // specified job id, ignore -J, -m, -q, -P and -u options

        requeRequest.number = 4;
        requeRequest.keyvalues = (struct keyvalue *)calloc(4, sizeof(struct keyvalue));
        if (NULL == requeRequest.keyvalues) {
            fprintf(stderr, "calloc(%ld) failed due to %m.\n",
                    requeRequest.number*sizeof(struct keyvalue));
            exit(4);
        }

        requeRequest.keyvalues[0].key = REQUEST_KEY_SIGNAL;
        requeRequest.keyvalues[0].value = (char *)calloc(12, sizeof(char));
        if (NULL == requeRequest.keyvalues[0].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
            exit(4);
        }
        snprintf(requeRequest.keyvalues[0].value, 12, "%d", QUICKPOOL_SIGKILL_REQUEUE);

        unsigned int size = numJobIds*sizeof(JOBID_T) + 10;
        requeRequest.keyvalues[1].key = REQUEST_KEY_JOBIDS;
        requeRequest.keyvalues[1].value = (char *)calloc(size+1, sizeof(char));
        if (NULL == requeRequest.keyvalues[1].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", size);
            exit(4);
        }

        int i;
        for (i = 0; i < numJobIds; i++) {
            if (0 == i) {
                snprintf(requeRequest.keyvalues[1].value, size, "%lld", jobIds[i]);
                continue;
            }

            unsigned int lenValue;
            lenValue = strlen(requeRequest.keyvalues[1].value);
            snprintf(requeRequest.keyvalues[1].value+lenValue, size, " %lld", jobIds[i]);
        }
        FREEUP(jobIds);

        requeRequest.keyvalues[2].key = REQUEST_KEY_OPTIONS;
        requeRequest.keyvalues[2].value = (char *)calloc(12, sizeof(char));
        if (NULL == requeRequest.keyvalues[2].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
            exit(4);
        }
        snprintf(requeRequest.keyvalues[2].value, 12, "%d", options);

        requeRequest.keyvalues[3].key = REQUEST_KEY_JOBSTATUS;
        requeRequest.keyvalues[3].value = (char *)calloc(12, sizeof(char));
        if (NULL == requeRequest.keyvalues[3].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
            exit(4);
        }
        snprintf(requeRequest.keyvalues[3].value, 12, "%d", status);
    } else {
        unsigned int num;

        num = 3; //sigValue, options, status

        if (NULL != strQueue) {
            num ++;
        }

        if (NULL != strNode) {
            num ++;
        }

        num ++; //username

        if (NULL != strJobName) {
            num ++;
        }

        if (NULL != strProject) {
            num ++;
        }

        requeRequest.number = num;
        requeRequest.keyvalues = (struct keyvalue *)calloc(num, sizeof(struct keyvalue));
        if (NULL == requeRequest.keyvalues) {
            fprintf(stderr, "calloc(%ld) failed due to %m.\n",
                    requeRequest.number*sizeof(struct keyvalue));
            exit(4);
        }

        num = 0;
        requeRequest.keyvalues[num].key = REQUEST_KEY_SIGNAL;
        requeRequest.keyvalues[num].value = (char *)calloc(12, sizeof(char));
        if (NULL == requeRequest.keyvalues[num].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
            exit(4);
        }
        snprintf(requeRequest.keyvalues[num].value, 12, "%d", QUICKPOOL_SIGKILL_REQUEUE);
        num ++;

        requeRequest.keyvalues[num].key = REQUEST_KEY_OPTIONS;
        requeRequest.keyvalues[num].value = (char *)calloc(12, sizeof(char));
        if (NULL == requeRequest.keyvalues[num].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
            exit(4);
        }
        snprintf(requeRequest.keyvalues[num].value, 12, "%d", options);
        num ++;

        requeRequest.keyvalues[num].key = REQUEST_KEY_JOBSTATUS;
        requeRequest.keyvalues[num].value = (char *)calloc(12, sizeof(char));
        if (NULL == requeRequest.keyvalues[num].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
            exit(4);
        }
        snprintf(requeRequest.keyvalues[num].value, 12, "%d", status);
        num ++;

        if (NULL != strQueue) {
            requeRequest.keyvalues[num].key = REQUEST_KEY_QUEUENAME;
            requeRequest.keyvalues[num].value = copy_string(strQueue);

            num ++;
        }

        if (NULL != strNode) {
            requeRequest.keyvalues[num].key = REQUEST_KEY_HOSTNAME;
            requeRequest.keyvalues[num].value = copy_string(strNode);

            num ++;
        }

        requeRequest.keyvalues[num].key = REQUEST_KEY_USERNAME;
        if (NULL == strUser) { //If not specify -u, use my name.
            requeRequest.keyvalues[num].value = getmy_uname();
        } else {
            requeRequest.keyvalues[num].value = copy_string(strUser);
        }
        num ++;

        if (NULL != strJobName) {
            requeRequest.keyvalues[num].key = REQUEST_KEY_JOBNAME;
            requeRequest.keyvalues[num].value = copy_string(strJobName);

            num ++;
        }

        if (NULL != strProject) {
            requeRequest.keyvalues[num].key = REQUEST_KEY_SUBMIT_PROJECT;
            requeRequest.keyvalues[num].value = copy_string(strProject);

            num ++;
        }
    }

    struct QuickPoolJobReply *jobReply;
    jobReply = client_signaljob(&requeRequest, NULL);
    if (NULL == jobReply) {

        gf_xdr_free(gf_xdr_request, &requeRequest);
        fprintf(stderr, "Faild to requeue job due to above reason.\n");

        exit(3);
    }

    gf_xdr_free(gf_xdr_request, &requeRequest);

    unsigned int i;
    for (i = 0; i < jobReply->num_job; i++) {
        char strJobId[32];
        fprintf(stdout, "Job <%s> is being requeued.\n",
                jobid_2string(jobReply->jobIDs[i], strJobId));
    }

    for (i = 0; i < jobReply->num_error; i++) {
        fprintf(stderr, "%s\n", jobReply->error_messages[i]);
    }

    if (0 < jobReply->num_error) {
        gf_xdr_free(libxdr_job_reply, jobReply);
        FREEUP(jobReply);
        exit(3);
    }

    gf_xdr_free(libxdr_job_reply, jobReply);
    FREEUP(jobReply);

    exit(0);
} // end function cmd_requeue_job

static void
requeuejob_usage(const char *cmd)
{
    fprintf(stdout, "qp job %s usage: [-h, --help]", cmd);
    fprintf(stdout, " [-a, --all] [-d, --done] [-e, --exit] [-r, --start]\n");
    fprintf(stdout, "\t\t      [-H, --hold] [-u, --user user_name|all] [-J, --jobname job_name]\n");
    fprintf(stdout, "\t\t      [-q, --queue queue_name] [-m, --node node_name]\n");
    fprintf(stdout, "\t\t      [-P, --project project_name] [jobId | \"jobId[idxList]\" ...]\n ");

    return;
}
